package cn.xinfei.xdecision.engine.runner.executor.handler;

import cn.hutool.core.collection.CollectionUtil;
import cn.xinfei.xdecision.common.model.component.ComponentAction;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleInfo;
import cn.xinfei.xdecision.common.model.configcenter.rejectcode.model.RejectBranchCodeModel;
import cn.xinfei.xdecision.common.model.configcenter.rejectcode.model.RejectDiversionLabelModel;
import cn.xinfei.xdecision.common.model.enginex.model.RuleHitModel;
import cn.xinfei.xdecision.common.model.enginex.model.RuleSetDecisionResultData;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.component.componentaction.ComponentActionService;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.util.DateUtils;
import cn.xinfei.xdecision.engine.runner.context.AmmunitionDepot;
import cn.xinfei.xdecision.engine.runner.context.InternalVariableConstant;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.executor.AbsNodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import cn.xinfei.xdecision.engine.runner.metadata.MetadataProvider;
import cn.xinfei.xdecision.engine.runner.vo.SelectedRuleVo;
import cn.xinfei.xdecision.httpclient.RestTemplateConfig;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dianping.cat.Cat;
import io.micrometer.core.instrument.Metrics;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.ENGINE_POST_BLACK_LIST_ADD_ERROR;

@Slf4j
@Service
public class RulePostProcessHandler {

    public static final String ACTION_CACHE_PRE = "action_cache_pre_";

    @Resource
    private ComponentActionService componentActionService;

    @Value("${xdecision.rcs.list.domain}")
    private String domain;

    @Value("${xdecision.rcs.list.addbatch}")
    private String listAddUrl;

    @Autowired
    protected MetadataProvider metadataProvider;

    @Resource
    private RestTemplate restTemplate;

    /**
     * 处理动作配置类
     */
    public void actionPostProcess(AbsNodeHandler nodeHandler, String nodeCode) {
        try {

            List<RuleHitModel> hitResultList = PipelineContextHolder.getHitResultList();
            if (CollectionUtils.isEmpty(hitResultList)) {
                return;
            }
            String actionCacheKey = ACTION_CACHE_PRE + nodeCode;
            //本地缓存
            List<ComponentAction> actionRequest = (ArrayList) nodeHandler.get(actionCacheKey);
            if (CollectionUtils.isEmpty(actionRequest)) {
                return;
            }
            //找出匹配的动作集合
            List<ComponentAction> ruleList = actionRequest.stream().filter(item ->
                            hitResultList.stream().anyMatch(hitResult ->
                                    Objects.equals(hitResult.getRuleCode(), item.getComponentCode()) &&
                                            Objects.equals(hitResult.getVersionNo(), item.getVersionNo())))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ruleList)) {
                return;
            }
            JSONArray ja = new JSONArray();
            //以动作code,动作类型，动作有效期分组
            Map<String, Set<String>> componentActionMap = ruleList.stream().collect(
                    Collectors.groupingBy(x -> x.getComponentCode() + SysConstant.COMMA +
                            x.getActionType() + SysConstant.COMMA +
                            x.getDuration(), Collectors.mapping(ComponentAction::getVarCode, Collectors.toSet())));
            componentActionMap.forEach((key, value) -> {
                JSONObject actionContext = new JSONObject();
                value.forEach(varCode -> {
                    //封装变量值
//                    Object valueByKey = PipelineContextHolder.getValueByKey(varCode);
                    Object valueByKey = AmmunitionDepot.getValueByKeyFromRemote(varCode);
                    if (Objects.nonNull(valueByKey)) {
                        actionContext.put(varCode, valueByKey);
                    }
                });
                if (actionContext.isEmpty()) {
                    return;
                }
                String[] split = key.split(SysConstant.COMMA);
                //目前只有黑名单,后续建立实体类，动作用枚举 todo 周鼎炎
                JSONObject jo = new JSONObject();
                jo.put("ruleCode", split[0]);
                //写死1（黑名单）
                jo.put(SysConstant.LIST_ID, "1");
                jo.put(SysConstant.FROM, "规则触发");
                jo.put(SysConstant.REASON, split[0]);
                jo.put(SysConstant.KEY_VALUE, actionContext);
                //开始时间
                String sfyCurrentDateTime = DateUtils.getSfyCurrentDateTime();
                //结束时间
                String endTime = Integer.parseInt(split[2]) == -1 ? null : DateUtils.getDateAfterDays(sfyCurrentDateTime, Integer.parseInt(split[2]));
                jo.put(SysConstant.START_TIME, sfyCurrentDateTime);
                jo.put(SysConstant.END_TIME, endTime);
                ja.add(jo);
            });
            if (CollectionUtils.isEmpty(ja)) {
                return;
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("data", ja);
            RestTemplateConfig.post(domain + listAddUrl, jsonObject.toJSONString(), ENGINE_POST_BLACK_LIST_ADD_ERROR);
        } catch (Exception e) {
            log.error("调用动作配置服务失败", e);
            Cat.logError(e); // 将异常上报到cat上
            Metrics.counter("xengine_action_total", "action", "list-server").increment();
        }
    }

    /**
     * 将动作配置信息存到缓存中
     */
    public List<ComponentAction> combineAction(List<RuleInfo> ruleInfoList) {
        List<ComponentAction> componentActionList = new ArrayList<>();
        //查询全部配置信息
        ruleInfoList.forEach(x ->
                componentActionList.addAll(
                        componentActionService.getComponentActionByComponentCode(x.getCode(), x.getVersionNo(), null)));
        return componentActionList;
    }


    public StateTransmitModel postProcess(StateTransmitModel stateTransmitModel, NodeHandler nodeHandler) {
        String nodeCode = nodeHandler.getCode();
        EngineNode engineNode = nodeHandler.getEngineNode();

//        XDecisionContext context = XDecisionContext.getContext();
//        StateTransmitModel stateTransmitModel = context.getStateContext();
        List<RuleHitModel> ruleHitListAll = stateTransmitModel.getHitResultList();
        List<RuleHitModel> ruleHitList = ruleHitListAll.stream().filter(ruleHitModel -> ruleHitModel.isRuleHitFlag()).collect(Collectors.toList());

        String hitRuleCodes = ruleHitList.stream().map(b -> b.getRuleCode()).collect(Collectors.joining(","));
        log.info("规则组执行结果，nodeCode={},executeResult={},ruleHitSize={}", nodeCode, hitRuleCodes, ruleHitList.size());
        boolean terminalCondition = false;
        String nextNodeCode = nodeHandler.getEngineNode().getNextNodeCodes();


        if (StringUtils.isNotEmpty(engineNode.getNodeScript())) {
            //如果没有选择规则，则不执行，直接继承
            JSONObject nodeScript = JSONObject.parseObject(engineNode.getNodeScript());
            JSONObject terminationInfo = nodeScript.getJSONObject("terminationInfo");
            JSONArray selectedRule = terminationInfo.getJSONArray("selectedRule");
            if (null != selectedRule && !selectedRule.isEmpty()) {
                //如果选择了规则，但是没有命中,默认是0
                long hitNum = 0;
                long scoreSum = 0l;
                if (CollectionUtil.isNotEmpty(ruleHitList)) {
                    List<SelectedRuleVo> selectedRuleList = JSONObject.parseArray(JSONObject.toJSONString(selectedRule), SelectedRuleVo.class);
                    // 查找已选规则中命名的规则集合
                    for (SelectedRuleVo selectedRuleVo : selectedRuleList) {
                        hitNum = hitNum + ruleHitList.stream().filter(ruleInfo -> ruleInfo.getRuleId().equals(selectedRuleVo.getId())).map(r -> r.getRuleId()).distinct().count();
                        scoreSum = scoreSum + ruleHitList.stream().filter(ruleInfo -> ruleInfo.getRuleId().equals(selectedRuleVo.getId())).mapToLong(r -> r.getRuleScore()).sum();
                    }
                }
                //先计算出命中规则的数量和分数，然后进入终止条件判断
                PipelineContextHolder.putInContext(nodeCode, InternalVariableConstant.RULE_HIT_NUM.getCode(), hitNum);
                PipelineContextHolder.putInContext(nodeCode, InternalVariableConstant.RULE_TOTAL_SCORE.getCode(), scoreSum);
                stateTransmitModel.getNodeOutput().put(InternalVariableConstant.RULE_HIT_NUM.getCode(), hitNum);
                stateTransmitModel.getNodeOutput().put(InternalVariableConstant.RULE_TOTAL_SCORE.getCode(), scoreSum);
                log.info("规则组命中情况，hitNum={}, scoreTotal={},terminalCondition={}", hitNum, scoreSum, terminalCondition);
            }
        }

        stateTransmitModel.setNextNodeCode(nextNodeCode);
        return stateTransmitModel;
    }


    public RuleSetDecisionResultData processRejectCode(StateTransmitModel stateTransmitModel, List<RuleInfo> ruleInfoList) {
        RuleSetDecisionResultData ruleSetDecisionResultData = new RuleSetDecisionResultData();

//        List<RuleHitModel> ruleHitList = PipelineContextHolder.getHitResultList();
        List<RuleHitModel> ruleHitListAll = stateTransmitModel.getHitResultList();
        if (CollectionUtil.isNotEmpty(ruleHitListAll)) {
            try {
                //真的命中的规则
                List<RuleHitModel> ruleHitModelList = ruleHitListAll.stream().filter(ruleHitModel -> ruleHitModel.isRuleHitFlag()).collect(Collectors.toList());
                if (ruleHitModelList != null && ruleHitModelList.size() > 0) {
                    RuleHitModel lastHitRule = ruleHitModelList.get(ruleHitModelList.size() - 1);
                    String ruleCode = lastHitRule.getRuleCode();
                    String name = lastHitRule.getRuleName();

                    //最后一个命中的规则名
                    ruleSetDecisionResultData.setFinalRejectName(name);

                    Map<Long, RuleInfo> ruleInfoMap = ruleInfoList.stream().collect(Collectors.toMap(rule -> rule.getId(), rule -> rule, (key1, key2) -> key2));
                    Set<Long> hitRejectBranchId = new HashSet<>();
                    for (RuleHitModel ruleHitModel : ruleHitModelList) {
                        Long ruleId = ruleHitModel.getRuleId();
                        RuleInfo ruleInfo = ruleInfoMap.get(ruleId);
                        if (null != ruleInfo) {
                            Long rejectBranchCodeId = ruleInfo.getRejectBranchCodeId();
                            hitRejectBranchId.add(rejectBranchCodeId);
                        } else {
                            log.warn("ruleInfoMap not container ruleId={}", ruleId);
                        }
                    }


                    //最大禁申天数
                    Map<Long, RejectBranchCodeModel> rejectBranchCodeInfo = metadataProvider.getRejectBranchCodeInfo();
                    try {
                        Optional<Map.Entry<Long, RejectBranchCodeModel>> max = rejectBranchCodeInfo.entrySet()
                                .stream()
                                .filter(b -> hitRejectBranchId.contains(b.getKey()))
                                .max(Comparator.comparingInt(b -> b.getValue().getProhibitedApplyTime()));
                        if (max.isPresent()) {
                            Map.Entry<Long, RejectBranchCodeModel> longRejectBranchCodeModelEntry = max.get();
                            RejectBranchCodeModel rejectBranchCodeModel = longRejectBranchCodeModelEntry.getValue();
                            if(rejectBranchCodeModel!=null) {
                                Integer prohibitedApplyTime = rejectBranchCodeModel.getProhibitedApplyTime();
                                ruleSetDecisionResultData.setProhibitedApplyTime(prohibitedApplyTime);
                            }else{
                                log.info("拒绝分支码不存在,rejectBranchCodeModel={}",rejectBranchCodeModel);
                            }
                        }
                    } catch (Exception e) {
                        log.error("解析最大禁申天数异常，rejectBranchCodeInfo={}", rejectBranchCodeInfo);
                    }

                    //导流标签按照优先级输出
                    try {
                        Optional<Map.Entry<Long, RejectBranchCodeModel>> min = rejectBranchCodeInfo.entrySet()
                                .stream()
                                .filter(b -> hitRejectBranchId.contains(b.getKey()))
                                .min(Comparator.comparingInt(b -> b.getValue().getRejectDiversionLabel().getWeights()));

                        if (min.isPresent()) {
                            Map.Entry<Long, RejectBranchCodeModel> longRejectBranchCodeModelEntry = min.get();
                            RejectBranchCodeModel value = longRejectBranchCodeModelEntry.getValue();
                            RejectDiversionLabelModel rejectDiversionLabel = value.getRejectDiversionLabel();
                            if(rejectDiversionLabel!=null) {
                                String diversionLabelName = rejectDiversionLabel.getName();
                                ruleSetDecisionResultData.setDiversionLabel(diversionLabelName);
                            }else{
                                log.info("导流标签不存在,RejectBranchCodeModel={}",value);
                            }
                        }
                    } catch (Exception e) {
                        log.error("解析导流标签异常，rejectBranchCodeInfo={}", rejectBranchCodeInfo);
                    }

                    //拒绝分支name
                    try {
                        String rejectBranchNames = rejectBranchCodeInfo.entrySet()
                                .stream()
                                .filter(b -> hitRejectBranchId.contains(b.getKey()))
                                .map(b -> b.getValue().getName())
                                .collect(Collectors.joining(","));
                        ruleSetDecisionResultData.setRejectBranchNames(rejectBranchNames);
                    } catch (Exception e) {
                        log.error("解析拒绝分支name异常，rejectBranchCodeInfo={}", rejectBranchCodeInfo);
                    }

                    //拒绝码name
                    try {
                        String rejectNames = rejectBranchCodeInfo.entrySet()
                                .stream()
                                .filter(b -> hitRejectBranchId.contains(b.getKey()))
                                .map(b -> b.getValue().getRejectCode().getName())
                                .collect(Collectors.joining(","));
                        ruleSetDecisionResultData.setRejectNames(rejectNames);
                    } catch (Exception e) {
                        log.error("解析拒绝name异常，rejectBranchCodeInfo={}", rejectBranchCodeInfo);
                    }

                    //设置out
//                PipelineContextHolder.putInContext(ruleCode,"branchReject", ruleSetDecisionResultData);
//                stateTransmitModel.getNodeOutput().put("branchReject", ruleSetDecisionResultData);
                    //暂时不去重，看看情况
                    List<RuleSetDecisionResultData> branchRejectInfo = PipelineContextHolder.getBranchRejectInfo();
                    branchRejectInfo.add(ruleSetDecisionResultData);

                    CopyOnWriteArrayList<RuleSetDecisionResultData> stateTransmitModelBranchRejectInfo = stateTransmitModel.getBranchRejectInfo();
                    stateTransmitModelBranchRejectInfo.add(ruleSetDecisionResultData);
                }
            } catch (Exception e) {
                log.error("拒绝分支码处理异常,msg={},ruleHitListAll={}", e.getMessage(), JSONObject.toJSONString(ruleHitListAll), e);
            }
        }

        return ruleSetDecisionResultData;
    }


}
